package com.xuecheng.media.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xuecheng.base.exception.StudySystemException;
import com.xuecheng.base.model.PageParams;
import com.xuecheng.base.model.PageResult;
import com.xuecheng.base.model.RestResponse;
import com.xuecheng.media.mapper.MediaFilesMapper;
import com.xuecheng.media.model.dto.QueryMediaParamsDto;
import com.xuecheng.media.model.dto.UploadFileParamsDto;
import com.xuecheng.media.model.dto.UploadFileResultDto;
import com.xuecheng.media.model.po.MediaFiles;
import com.xuecheng.media.service.MediaFileService;
import com.xuecheng.media.task.VideoFileProcessor;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FilterInputStream;
import java.time.LocalDateTime;
import java.util.Objects;

import static com.xuecheng.media.utils.MinIOFileOperationUtils.*;


/**
 * @description TODO
 * @author Mr.M
 * @date 2022/9/10 8:58
 * @version 1.0
 */
@Slf4j
@Service
public class MediaFileServiceImpl implements MediaFileService {
    private final MediaFilesMapper mediaFilesMapper;
    private final MinioClient minioClient;
    private final MediaFileService mediaFileService;
    private final VideoFileProcessor videoFileProcessor;
    public MediaFileServiceImpl(MediaFilesMapper mediaFilesMapper,
                                MinioClient minioClient,
                                @Lazy MediaFileService mediaFileService,
                                VideoFileProcessor videoFileProcessor) {
        this.mediaFilesMapper = mediaFilesMapper;
        this.minioClient = minioClient;
        this.mediaFileService = mediaFileService;
        this.videoFileProcessor = videoFileProcessor;
    }

    /** 存储图片 **/
    @Value("${minio.bucket.files}")
    private String BUCKET_NAME_MEDIA_FILES;
    /** 存储视频 **/
    @Value("${minio.bucket.videofiles}")
    private String BUCKET_NAME_VIDEO;

    @Override
    public PageResult<MediaFiles> queryMediaFiels(Long companyId,
                                                  PageParams pageParams,
                                                  QueryMediaParamsDto queryMediaParamsDto) {
        // 查询数据内容获得结果
        Page<MediaFiles> pageResult = mediaFilesMapper.selectPage(new Page<>(pageParams.getPageNo(),
                pageParams.getPageSize()),
                new LambdaQueryWrapper<>());
        // 构建结果集
        return new PageResult<>(pageResult.getRecords(),
                pageResult.getTotal(),
                pageParams.getPageNo(),
                pageParams.getPageSize());
    }

    @SneakyThrows
    @Override
    public UploadFileResultDto uploadCoursePic(Long companyId,
                                               String localFilePath,
                                               UploadFileParamsDto uploadFileParamsDto,
                                               String objectName) {
        String filename = uploadFileParamsDto.getFilename();
        String extension = filename.substring(filename.lastIndexOf("."));
        String mimeType = getMimeType(extension);
        String md5 = getFileContentMD5(new File(localFilePath));
        if (StringUtils.isEmpty(objectName)) {
             objectName = getDefaultFolderPath() + md5 + extension;
        }
        boolean isSuccess = uploadFileToMinio(BUCKET_NAME_MEDIA_FILES, localFilePath, objectName, mimeType);
        if (!isSuccess) {
            StudySystemException.cast("上传文件失败");
        }
        MediaFiles mediaFiles = mediaFileService.fileInfoToDataBase(md5,
                uploadFileParamsDto,
                companyId,
                BUCKET_NAME_MEDIA_FILES,
                objectName);
        if (Objects.isNull(mediaFiles)) {
            StudySystemException.cast("保存文件信息失败");
        } else {
            int update = mediaFilesMapper.updateById(mediaFiles);
            if (update <= 0) {
                StudySystemException.cast("更新文件信息失败");
            }
        }
        UploadFileResultDto uploadFileResultDto = new UploadFileResultDto();
        BeanUtils.copyProperties(mediaFiles, uploadFileResultDto);
        return uploadFileResultDto;
    }

    @Override
    @Transactional
    public MediaFiles fileInfoToDataBase(String md5,
                                   UploadFileParamsDto uploadFileParamsDto,
                                   Long companyId,
                                   String bucketName,
                                   String objectName) {
        // 将文件存储到数据库中
        MediaFiles mediaFiles = mediaFilesMapper.selectById(md5);
        if (Objects.isNull(mediaFiles)) {
            mediaFiles = new MediaFiles();
            BeanUtils.copyProperties(uploadFileParamsDto, mediaFiles);
            uploadFileParamsDto = null;
            // 文件id
            mediaFiles.setId(md5);
            mediaFiles.setCompanyId(companyId);
            mediaFiles.setBucket(bucketName);
            mediaFiles.setFilePath(objectName);
            mediaFiles.setFileId(md5);
            mediaFiles.setUrl("/" + bucketName + "/" + objectName);
            // 上传时间
            mediaFiles.setCreateDate(LocalDateTime.now());
            mediaFiles.setStatus("1");
            // 审核状态
            mediaFiles.setAuditStatus("002003");
            int insert = mediaFilesMapper.insert(mediaFiles);
            if (insert <= 0) {
                log.error("向数据库存储文件信息失败, bucketName:{}, objectName:{}", bucketName, objectName);
                return null;
            }
        }
        // 记录待处理任务
        videoFileProcessor.addVideoFileProcessTask(mediaFiles);
        return mediaFiles;
    }

    @Override
    public RestResponse<Boolean> checkFile(String fileMd5) {
        // 查询数据库
        MediaFiles file = mediaFilesMapper.selectById(fileMd5);
        if (Objects.isNull(file)) {
            return RestResponse.success(false);
        }
        try (FilterInputStream fi = minioClient.getObject(GetObjectArgs
                .builder()
                .bucket(file.getBucket())
                .object(file.getFilePath())
                .build())) {
            return RestResponse.success(fi != null);
        } catch (Exception e) {
            log.error("检测文件是否存在于MinIO存在错误 {}", e.getLocalizedMessage());
            return RestResponse.success(false);
        }
    }

    @Override
    public RestResponse<Boolean> checkChunk(String fileMd5, int chunk) {
        String minIOChunkFilePath = getMinIOChunkFilePath(fileMd5);
        if (StringUtils.isEmpty(minIOChunkFilePath)) {
            StudySystemException.cast("md5不能为空");
        }
        try (FilterInputStream fi = minioClient.getObject(GetObjectArgs.builder()
                .bucket(BUCKET_NAME_VIDEO)
                .object(minIOChunkFilePath + chunk)
                .build())) {
            return RestResponse.success(fi != null);
        } catch (Exception e) {
            log.error("检测分块文件是否存在于MinIO存在错误 {}", e.getLocalizedMessage());
            return RestResponse.success(false);
        }
    }

    @Override
    public RestResponse uploadChunk(String fileMd5,
                                    int chunk,
                                    String localFilePath) {
        String mimeType = getMimeType(null);
        String chunkFilePath = getMinIOChunkFilePath(fileMd5) + chunk;
        boolean isSuccess = uploadFileToMinio(BUCKET_NAME_VIDEO, localFilePath, chunkFilePath, mimeType);
        return isSuccess ?
                RestResponse.success(isSuccess) :
                RestResponse.validfail(false,"上传文件失败");
    }

    @Override
    public RestResponse mergeChunks(Long companyId,
                                    String fileMd5,
                                    String fileName,
                                    int chunkTotal) {
        UploadFileParamsDto uploadFileParamsDto = new UploadFileParamsDto();
        uploadFileParamsDto.setFilename(fileName);
        uploadFileParamsDto.setTags("视频文件");
        uploadFileParamsDto.setFileType("001002");

        String chunkFilePath = getMinIOChunkFilePath(fileMd5);
        String extension = fileName.substring(fileName.lastIndexOf("."));
        String objectName = getMinIOMergeFilePath(fileMd5, extension);
        mergeMinIOFile(chunkTotal, chunkFilePath, objectName);
        // 合并后的文件与原文件进行对比
        File file = downFileFromMinIO(BUCKET_NAME_VIDEO, objectName);
        if (Objects.isNull(file)) {
            log.error("从MinIO下载文件失败 bucketName:{} objectName:{} 错误信息:file value is null",
                    BUCKET_NAME_VIDEO,
                    objectName);
            return RestResponse.validfail(false, "从MinIO下载文件失败");
        }
        uploadFileParamsDto.setFileSize(file.length());
        verifyMinIOFile(file, fileMd5);
        // 文件信息入库
        MediaFiles mediaFiles = mediaFileService.fileInfoToDataBase(fileMd5,
                uploadFileParamsDto,
                companyId,
                BUCKET_NAME_VIDEO,
                objectName);
        if (Objects.isNull(mediaFiles)) {
            log.error("文件信息入库失败, MediaFiles object value is null!");
            return RestResponse.validfail(false, "文件入库失败");
        }
        clearMinIOChunkFile(chunkFilePath, chunkTotal);
        return RestResponse.success(true);
    }

    @Override
    public MediaFiles getVideoFileById(String mediaId) {
        return mediaFilesMapper.selectById(mediaId);
    }
}
